home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / sd-26.zip / sdmain.c < prev    next >
C/C++ Source or Header  |  1992-09-09  |  56KB  |  1,567 lines

  1. /* SD -- square dance caller's helper.
  2.  
  3.     Copyright (C) 1990, 1991, 1992  William B. Ackerman.
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation; either version 1, or (at your option)
  8.     any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  
  19.     The version of this file is as shown immediately below.  This string
  20.     gets displayed at program startup. */
  21.  
  22. #define VERSION_STRING "26.0"
  23.  
  24. /* This defines the following functions:
  25.    main
  26.    mark_parse_blocks
  27.    release_parse_blocks_to_mark
  28.    initialize_parse
  29.    save_parse_state
  30.    restore_parse_state
  31.    copy_parse_tree
  32.    deposit_call
  33.    deposit_concept
  34.    query_for_call
  35.    get_real_subcall
  36.  
  37. and the following external variables:
  38.    abs_max_calls
  39.    max_base_calls
  40.    base_calls
  41.    outfile_string
  42.    last_file_position
  43.    global_age
  44.    parse_state
  45.    uims_menu_index
  46.    major_database_version
  47.    minor_database_version
  48.    whole_sequence_low_lim
  49.    not_interactive
  50.    initializing_database
  51.    testing_fidelity
  52.    selector_for_initialize
  53. */
  54.  
  55.  
  56. #include <stdio.h>
  57. #include <string.h>
  58. #include "sd.h"
  59.  
  60. /* These variables are external. */
  61.  
  62. int abs_max_calls;
  63. int max_base_calls;
  64. callspec_block **base_calls;        /* Gets allocated as array of pointers in sdinit. */
  65. char outfile_string[80];
  66. int last_file_position = -1;
  67. int global_age;
  68. parse_state_type parse_state;
  69. int uims_menu_index;
  70. char major_database_version[20];
  71. char minor_database_version[20];
  72. int whole_sequence_low_lim;
  73. long_boolean not_interactive = FALSE;
  74. long_boolean initializing_database = FALSE;
  75. long_boolean testing_fidelity = FALSE;
  76. selector_kind selector_for_initialize;
  77.  
  78. /* These variables are are global to this file. */
  79.  
  80. static uims_reply reply;
  81. static long_boolean reply_pending;
  82. static int error_flag;
  83. static parse_block *parse_active_list;
  84. static parse_block *parse_inactive_list;
  85. static int allowing_modifications;
  86. static int concept_sublist_sizes[NUM_CALL_LIST_KINDS];
  87. static short int *concept_sublists[NUM_CALL_LIST_KINDS];
  88.  
  89.  
  90. /* Stuff for saving parse state while we resolve. */
  91.  
  92. static parse_state_type saved_parse_state;
  93. static parse_block *saved_command_root;
  94.  
  95.  
  96.  
  97.  
  98. /* This static variable is used by main. */
  99.  
  100. static concept_descriptor centers_concept = {"centers????", concept_centers_or_ends, l_mainstream, {0, 0}};
  101.  
  102.  
  103. /* This fills in concept_sublist_sizes and concept_sublists. */
  104.  
  105. /*   A "1" means the concept is allowed in this setup
  106.  
  107.                      lout, lin, tby
  108.                            |
  109.                            |+- 8ch, Lcol, col
  110.             2fl, Lwv, wv -+||
  111.                           |||+- cdpt, dpt, L1x8
  112.         qtag, gcol, L2fl-+||||
  113.                          |||||+- 1x8, any, junk
  114.                          ||||||       */
  115. #define MASK_CTR_2      0600016
  116. #define MASK_QUAD_D     0200016
  117. #define MASK_CPLS       0547476
  118. #define MASK_TAND       0770362
  119. #define MASK_SIAM       0400002
  120.  
  121. static void initialize_concept_sublists(void)
  122. {
  123.    int number_of_concepts;
  124.    int concepts_at_level;
  125.    call_list_kind test_call_list_kind;
  126.    unsigned long int setup_mask;
  127.  
  128.    for (       number_of_concepts = 0, concepts_at_level = 0;
  129.                concept_descriptor_table[number_of_concepts].kind != marker_end_of_list;
  130.                number_of_concepts++) {
  131.       if (concept_descriptor_table[number_of_concepts].level <= calling_level)
  132.          concepts_at_level++;
  133.    }
  134.    general_concept_size = number_of_concepts - general_concept_offset;
  135.  
  136.    concept_sublist_sizes[call_list_any] = concepts_at_level;
  137.    concept_sublists[call_list_any] = (short int *) get_mem(concepts_at_level*sizeof(short int));
  138.  
  139.    /* Make the concept sublists, one per setup. */
  140.  
  141.    for (test_call_list_kind = call_list_qtag; test_call_list_kind > call_list_any; test_call_list_kind--) {
  142.       for (       number_of_concepts = 0, concepts_at_level = 0;
  143.                   concept_descriptor_table[number_of_concepts].kind != marker_end_of_list;
  144.                   number_of_concepts++) {
  145.          concept_descriptor *p = &concept_descriptor_table[number_of_concepts];
  146.  
  147.          if (p->level <= calling_level) {
  148.             setup_mask = ~0;
  149.             /* This concept is legal at this level.  see if it is appropriate for this setup.
  150.                If we don't know, the default value of setup_mask will make it legal. */
  151.             switch (p->kind) {
  152.                case concept_centers_or_ends: case concept_centers_and_ends:
  153.                   if (p->value.arg1 >= 2) {
  154.                      setup_mask = MASK_CTR_2;    /* This is a 6 and 2 type of concept. */
  155.                   }
  156.                   break;
  157.                case concept_tandem: case concept_some_are_tandem:
  158.                   if (!(p->value.arg2 | p->value.arg3)) {
  159.                      switch (p->value.arg4) {
  160.                         case 0:     /* tandem */
  161.                            setup_mask = MASK_TAND; break;
  162.                         case 1:     /* couples */
  163.                            setup_mask = MASK_CPLS; break;
  164.                         default:    /* siamese */
  165.                            setup_mask = MASK_SIAM; break;
  166.                      }
  167.                   }
  168.                   break;
  169.                case concept_quad_diamonds: case concept_quad_diamonds_together:
  170.                   setup_mask = MASK_QUAD_D;
  171.                   break;
  172.             }
  173.  
  174.             if ((1 << ((int) test_call_list_kind)) & setup_mask)
  175.                concept_sublists[call_list_any][concepts_at_level++] = number_of_concepts;
  176.          }
  177.       }
  178.  
  179.       concept_sublist_sizes[test_call_list_kind] = concepts_at_level;
  180.       concept_sublists[test_call_list_kind] = (short int *) get_mem(concepts_at_level*sizeof(short int));
  181.       (void) memcpy(concept_sublists[test_call_list_kind], concept_sublists[call_list_any], concepts_at_level*sizeof(short int));
  182.    }
  183.  
  184.    for (       number_of_concepts = 0, concepts_at_level = 0;
  185.                concept_descriptor_table[number_of_concepts].kind != marker_end_of_list;
  186.                number_of_concepts++) {
  187.       if (concept_descriptor_table[number_of_concepts].level <= calling_level)
  188.          concept_sublists[call_list_any][concepts_at_level++] = number_of_concepts;
  189.    }
  190. }
  191.  
  192.  
  193.  
  194.  
  195. extern parse_block *mark_parse_blocks(void)
  196. {
  197.    return parse_active_list;
  198. }
  199.  
  200.  
  201. extern void release_parse_blocks_to_mark(parse_block *mark_point)
  202. {
  203.    while (parse_active_list && parse_active_list != mark_point) {
  204.       parse_block *item = parse_active_list;
  205.  
  206.       parse_active_list = item->gc_ptr;
  207.       item->gc_ptr = parse_inactive_list;
  208.       parse_inactive_list = item;
  209.  
  210.       /* Clear pointers so we will notice if it gets erroneously re-used. */
  211.       item->concept = &mark_end_of_list;
  212.       item->call = base_calls[1];
  213.       item->subsidiary_root = (parse_block *) 0;
  214.       item->next = (parse_block *) 0;
  215.    }
  216. }
  217.  
  218.  
  219.  
  220.  
  221. static parse_block *get_parse_block(void)
  222. {
  223.    parse_block *item;
  224.  
  225.    if (parse_inactive_list) {
  226.       item = parse_inactive_list;
  227.       parse_inactive_list = item->gc_ptr;
  228.    }
  229.    else {
  230.       item = (parse_block *) get_mem(sizeof(parse_block));
  231.    }
  232.  
  233.    item->gc_ptr = parse_active_list;
  234.    parse_active_list = item;
  235.  
  236.    item->concept = (concept_descriptor *) 0;
  237.    item->call = (callspec_block *) 0;
  238.    item->selector = selector_uninitialized;
  239.    item->number = 0;
  240.    item->subsidiary_root = (parse_block *) 0;
  241.    item->next = (parse_block *) 0;
  242.  
  243.    return(item);
  244. }
  245.  
  246.  
  247.  
  248.  
  249.  
  250. extern void initialize_parse(void)
  251. {
  252.    parse_state.concept_write_base = &history[history_ptr+1].command_root;
  253.    parse_state.concept_write_ptr = parse_state.concept_write_base;
  254.    *parse_state.concept_write_ptr = (parse_block *) 0;
  255.    parse_state.call_list_to_use = find_proper_call_list(&history[history_ptr].state);
  256.    history[history_ptr+1].warnings.bits[0] = 0;
  257.    history[history_ptr+1].warnings.bits[1] = 0;
  258.    history[history_ptr+1].draw_pic = FALSE;
  259.  
  260.    if (written_history_items > history_ptr)
  261.       written_history_items = history_ptr;
  262.  
  263.    parse_state.parse_stack_index = 0;
  264.    parse_state.specialprompt = (char *) 0;
  265.    parse_state.topcallflags = 0;
  266. }
  267.  
  268.  
  269.  
  270.  
  271.  
  272.  
  273. extern parse_block *copy_parse_tree(parse_block *original_tree)
  274. {
  275.    parse_block *new_item, *old_item, *new_root;
  276.  
  277.    old_item = original_tree;
  278.  
  279.    if (!old_item) return NULL;
  280.  
  281.    new_item = get_parse_block();
  282.    new_root = new_item;
  283.    for (;;) {
  284.       new_item->concept = old_item->concept;
  285.       new_item->call = old_item->call;
  286.       new_item->selector = old_item->selector;
  287.       new_item->number = old_item->number;
  288.  
  289.       if (old_item->subsidiary_root)
  290.          new_item->subsidiary_root = copy_parse_tree(old_item->subsidiary_root);
  291.  
  292.       if (!old_item->next) break;
  293.  
  294.       new_item->next = get_parse_block();
  295.       new_item = new_item->next;
  296.       old_item = old_item->next;
  297.    }
  298.  
  299.    return new_root;
  300. }
  301.  
  302.  
  303.  
  304. /* Save the entire parse stack, and make a copy of the dynamic blocks that
  305.    comprise the parse state. */
  306. extern void save_parse_state(void)
  307. {
  308.    saved_parse_state = parse_state;
  309.    saved_command_root = copy_parse_tree(history[history_ptr+1].command_root);
  310. }
  311.  
  312.  
  313. static void reset_parse_tree(
  314.    parse_block *original_tree,
  315.    parse_block *final_head)
  316.  
  317. {
  318.    parse_block *new_item, *old_item;
  319.  
  320.    new_item = final_head;
  321.    old_item = original_tree;
  322.    for (;;) {
  323.       new_item->concept = old_item->concept;
  324.       new_item->call = old_item->call;
  325.       new_item->selector = old_item->selector;
  326.       new_item->number = old_item->number;
  327.  
  328.       /* Chop off branches that don't belong. */
  329.  
  330.       if (!old_item->subsidiary_root)
  331.          new_item->subsidiary_root = 0;
  332.       else
  333.          reset_parse_tree(old_item->subsidiary_root, new_item->subsidiary_root);
  334.  
  335.       if (!old_item->next) {
  336.          new_item->next = 0;
  337.          break;
  338.       }
  339.  
  340.       new_item = new_item->next;
  341.       old_item = old_item->next;
  342.    }
  343. }
  344.  
  345.  
  346. /* Restore the parse state.  We write directly over the original dynamic blocks
  347.    of the current parse state (making use of the fact that the alterations that
  348.    could have happened will add to the tree but never delete anything.  This way,
  349.    after we have saved and restored things, they are all in their original,
  350.    locations, so that the pointers in the parse stack will still be valid. */
  351. extern long_boolean restore_parse_state(void)
  352. {
  353.    parse_state = saved_parse_state;
  354.  
  355.    if (saved_command_root)
  356.       reset_parse_tree(saved_command_root, history[history_ptr+1].command_root);
  357.    else
  358.       history[history_ptr+1].command_root = 0;
  359.  
  360.    return (FALSE);
  361. }
  362.  
  363.  
  364.  
  365.  
  366.  
  367. /* Deposit a call into the parse state.  A returned value of TRUE
  368.    means that the user refused to click on a required number or selector,
  369.    and so we have taken no action.  This can only occur if interactive.
  370.    If not interactive, stuff will be chosen by random number. */
  371.  
  372. extern long_boolean deposit_call(callspec_block *call)
  373. {
  374.    parse_block *new_block;
  375.    selector_kind sel = selector_uninitialized;
  376.    int number = 0;
  377.  
  378.    /* Put in selector and/or number as required. */
  379.  
  380.    if (call->callflags & cflag__requires_selector) {
  381.       int j;
  382.  
  383.       if (initializing_database)
  384.          sel = selector_for_initialize;              
  385.       else if (not_interactive)
  386.          sel = (selector_kind) generate_random_number(last_selector_kind)+1;
  387.       else if ((j = uims_do_selector_popup()) != 0)
  388.          sel = (selector_kind) j;
  389.       else
  390.          return(TRUE);
  391.    }
  392.  
  393.    if (call->callflags & cflag__requires_number) {
  394.       if (initializing_database)
  395.          /* If this wants a number, give it 1.  0 won't work for the call 1/4 the alter. */
  396.          number = 1;
  397.       else if (not_interactive)
  398.          number = generate_random_number(4)+1;
  399.       else if ((number = uims_do_quantifier_popup()) != 0)
  400.          ;
  401.       else
  402.          return(TRUE);
  403.    }
  404.  
  405.    new_block = get_parse_block();
  406.    new_block->concept = &mark_end_of_list;
  407.    new_block->call = call;
  408.    new_block->selector = sel;
  409.    new_block->number = number;
  410.  
  411.    parse_state.topcallflags = call->callflags;
  412.    *parse_state.concept_write_ptr = new_block;
  413.  
  414.    return(FALSE);
  415. }
  416.  
  417.  
  418. /* Deposit a concept into the parse state.  A returned value of TRUE
  419.    means that the user refused to click on a required number or selector,
  420.    and so we have taken no action.  This can only occur if interactive.
  421.    If not interactive, stuff will be chosen by random number. */
  422.  
  423. extern long_boolean deposit_concept(concept_descriptor *conc)
  424. {
  425.    parse_block *new_block;
  426.    selector_kind sel = selector_uninitialized;
  427.    int number = 0;
  428.  
  429.    if (conc->level > calling_level) warn(warn__bad_concept_level);
  430.  
  431.    if (concept_table[conc->kind].concept_prop & CONCPROP__USE_SELECTOR) {
  432.       int j;
  433.  
  434.       if (not_interactive)
  435.          sel = (selector_kind) generate_random_number(last_selector_kind)+1;
  436.       else if ((j = uims_do_selector_popup()) != 0)
  437.          sel = (selector_kind) j;
  438.       else
  439.          return(TRUE);
  440.    }
  441.  
  442.    if (concept_table[conc->kind].concept_prop & CONCPROP__USE_NUMBER) {
  443.  
  444.       if (not_interactive)
  445.          number = generate_random_number(4)+1;
  446.       else if ((number = uims_do_quantifier_popup()) != 0)
  447.          ;
  448.       else
  449.          return(TRUE);
  450.  
  451.       if (concept_table[conc->kind].concept_prop & CONCPROP__USE_TWO_NUMBERS) {
  452.          int second_num;
  453.          if (not_interactive)
  454.             second_num = generate_random_number(4)+1;
  455.          else if ((second_num = uims_do_quantifier_popup()) != 0)
  456.             ;
  457.          else {
  458.             return(TRUE);
  459.          }
  460.          number |= second_num << 16;
  461.       }
  462.    }
  463.  
  464.    new_block = get_parse_block();
  465.    new_block->concept = conc;
  466.    new_block->selector = sel;
  467.    new_block->number = number;
  468.  
  469.    *parse_state.concept_write_ptr = new_block;
  470.  
  471.    /* See if we need to parse a second call for this concept. */
  472.  
  473.    if (concept_table[conc->kind].concept_prop & CONCPROP__SECOND_CALL) {
  474.       /* Set up recursion, reading the first call and its concepts into the same parse block. */
  475.  
  476.       if (parse_state.parse_stack_index == 39) specialfail("Excessive number of concepts.");
  477.       parse_state.parse_stack[parse_state.parse_stack_index].save_concept_kind = conc->kind;
  478.       parse_state.parse_stack[parse_state.parse_stack_index++].concept_write_save_ptr = parse_state.concept_write_ptr;
  479.       parse_state.specialprompt = (char *) 0;
  480.       parse_state.topcallflags = 0;          /* Erase anything we had -- it is meaningless now. */
  481.    }
  482.  
  483.    parse_state.call_list_to_use = call_list_any;
  484.  
  485.    /* Advance the write pointer. */
  486.    parse_state.concept_write_ptr = &(new_block->next);
  487.  
  488.    return(FALSE);
  489. }
  490.  
  491.  
  492. /* False result means OK.  Otherwise, user clicked on something special,
  493.    such as "abort" or "undo", and the reply tells what it was. */
  494.  
  495. extern long_boolean query_for_call(void)
  496. {
  497.    uims_reply local_reply;
  498.    callspec_block *result;
  499.  
  500.    recurse_entry:
  501.  
  502.    *parse_state.concept_write_ptr = (parse_block *) 0;   /* We should actually re-use anything there. */
  503.  
  504.    if (allowing_modifications)
  505.       parse_state.call_list_to_use = call_list_any;
  506.    
  507.    if (!not_interactive) {
  508.       /* We are operating in interactive mode.  Update the
  509.          display and query the user. */
  510.  
  511.       /* Error codes are:
  512.          1 - 1-line error message, text is in error_message1.
  513.          2 - 2-line error message, text is in error_message1 and error_message2.
  514.          3 - collision error, message is that people collided, they are in collision_person1 and collision_person2.
  515.          4 - "resolve" or similar command was called in inappropriate context, text is in error_message1.
  516.          5 - clicked on something inappropriate in subcall reader.
  517.          6 - unable-to-execute error, person is in collision_person1.
  518.          7 - wants to display stale call statistics. */
  519.  
  520.       if (error_flag == 7) {
  521.          clear_screen();
  522.          writestuff("***** LEAST RECENTLY USED 2% OF THE CALLS ARE:");
  523.          newline();
  524.          writestuff("filibuster     peel the toptivate");
  525.          newline();
  526.          writestuff("spin chain and circulate the gears    spin chain and exchange the gears");
  527.          newline();
  528.          error_flag = 0;
  529.          goto try_again;
  530.       }
  531.       
  532.       /* First, update the output area to the current state, with error messages, etc.
  533.          We draw a picture for the last two calls. */
  534.       
  535.       display_initial_history(history_ptr, 2);
  536.  
  537.       if (history[history_ptr].resolve_flag.kind != resolve_none) {
  538.          newline();
  539.          writestuff("     resolve is:");
  540.          newline();
  541.          writestuff(resolve_names[history[history_ptr].resolve_flag.kind]);
  542.          writestuff(resolve_distances[history[history_ptr].resolve_flag.distance & 7]);
  543.          newline();
  544.       }
  545.       
  546.       if (error_flag) {
  547.       
  548.          if (error_flag != 4) {
  549.             writestuff("Can't do this call:");
  550.             newline();
  551.             write_history_line(0, (char *) 0, FALSE, file_write_no);
  552.          }
  553.  
  554.          if (error_flag == 3) {
  555.                   /* very special message -- no text here, two people collided
  556.                      and they are stored in collision_person1 and collision_person2. */
  557.    
  558.             char person_string[3];
  559.    
  560.             writestuff("Some people (");
  561.             if ((collision_person1 | collision_person2) & BIT_VIRTUAL) {
  562.                writestuff("virtual) on same spot.");
  563.             }
  564.             else {
  565.                person_string[0] = ((collision_person1 >> 7) & 3) + '1';
  566.                person_string[1] = (collision_person1 & 0100) ? 'G' : 'B';
  567.                person_string[2] = '\0';
  568.                writestuff(person_string);
  569.                writestuff(" and ");
  570.                person_string[0] = ((collision_person2 >> 7) & 3) + '1';
  571.                person_string[1] = (collision_person2 & 0100) ? 'G' : 'B';
  572.                writestuff(person_string);
  573.                writestuff(", for example) on same spot.");
  574.             }
  575.          }
  576.          else if (error_flag == 4) {
  577.             writestuff(error_message1);
  578.          }
  579.          else if (error_flag <= 2) {
  580.             writestuff(error_message1);
  581.             if (error_flag == 2) {
  582.                newline();
  583.                writestuff("   ");
  584.                writestuff(error_message2);
  585.             }
  586.          }
  587.          else if (error_flag == 6) {
  588.                   /* very special message -- no text here, someone can't execute the
  589.                      call, and he is stored in collision_person1. */
  590.  
  591.             char person_string[3];
  592.    
  593.             writestuff("Some people (");
  594.             if (collision_person1 & BIT_VIRTUAL) {
  595.                writestuff("virtual) can't execute their part of this call.");
  596.             }
  597.             else {
  598.                person_string[0] = ((collision_person1 >> 7) & 3) + '1';
  599.                person_string[1] = (collision_person1 & 0100) ? 'G' : 'B';
  600.                person_string[2] = '\0';
  601.                writestuff(person_string);
  602.                writestuff(", for example) can't execute their part of this call.");
  603.             }
  604.          }
  605.          else           /* Must be 5, special signal for aborting out of subcall reader. */
  606.             writestuff("You can't select that here.");
  607.       
  608.          newline();
  609.       }
  610.  
  611.       error_flag = 0;
  612.  
  613.       /* Display the call index number, and the partially entered call and/or prompt, as appropriate. */
  614.  
  615.       {
  616.          char indexbuf[10];
  617.          sprintf (indexbuf, "%d: ", history_ptr-whole_sequence_low_lim+2);
  618.  
  619.          /* Echo the concepts entered so far.  This list is incomplete, so write_history_line has
  620.             to be careful. */
  621.          
  622.          if (parse_state.concept_write_ptr != &history[history_ptr+1].command_root) {
  623.             write_history_line(history_ptr+1, indexbuf, FALSE, file_write_no);
  624.          }
  625.          else {
  626.             writestuff(indexbuf);   /* Just write the index. */
  627.          }
  628.       }
  629.  
  630.       if (parse_state.specialprompt) {
  631.          writestuff(parse_state.specialprompt);
  632.       }
  633.  
  634.       newline();
  635.       
  636.       try_again:
  637.  
  638.       local_reply = uims_get_command(mode_normal, parse_state.call_list_to_use, allowing_modifications);
  639.  
  640.       if (local_reply == ui_concept_select) {
  641.          /* Fudge the number to be relative to the entire concept list. */
  642.          uims_menu_index += general_concept_offset;
  643.       }
  644.       else if (local_reply == ui_special_concept) {
  645.          int column, index, menu;
  646.  
  647.          menu = uims_menu_index;
  648.          if ((index = uims_do_concept_popup(menu)) == 0) goto try_again;
  649.          index--;
  650.          column = index >> 8;
  651.          index &= 0xFF;
  652.          index += concept_offset_tables[menu][column];
  653.          uims_menu_index = index;
  654.          /* Check for non-existent menu entry. */
  655.          if (concept_descriptor_table[uims_menu_index].kind == concept_comment) goto try_again;
  656.          local_reply = ui_concept_select;
  657.       }
  658.    }
  659.    else {
  660.  
  661.       /* We are operating in automatic mode.
  662.          We must insert a concept or a call.  Decide which. */
  663.  
  664.       if (generate_random_concept_p()) {
  665.          local_reply = ui_concept_select;
  666.          uims_menu_index = concept_sublists[parse_state.call_list_to_use][generate_random_number(concept_sublist_sizes[parse_state.call_list_to_use])];
  667.       }
  668.       else {
  669.          local_reply = ui_call_select;
  670.       }
  671.    }
  672.    
  673.    /* Now see what kind of command we have. */
  674.  
  675.    if (local_reply == ui_command_select) {
  676.       if (uims_menu_index == command_allow_modification) {
  677.          /* Increment "allowing_modifications" up to a maximum of 2. */
  678.          if (allowing_modifications != 2) allowing_modifications++;
  679.          goto recurse_entry;
  680.       }
  681.       else if (uims_menu_index == command_create_comment) {
  682.          char comment[80];
  683.    
  684.          if (uims_do_comment_popup(comment)) {
  685.             char *temp_text_ptr;
  686.             comment_block *new_comment_block;     /* ****** Kludge!!!!! */
  687.    
  688.             new_comment_block = (comment_block *) get_mem(sizeof(comment_block));
  689.             temp_text_ptr = &new_comment_block->txt[0];
  690.             string_copy(&temp_text_ptr, comment);
  691.  
  692.             *parse_state.concept_write_ptr = get_parse_block();
  693.             (*parse_state.concept_write_ptr)->concept = &marker_concept_comment;
  694.  
  695.             (*parse_state.concept_write_ptr)->call = (callspec_block *) new_comment_block;
  696.             /* Advance the write pointer. */
  697.             parse_state.concept_write_ptr = &((*parse_state.concept_write_ptr)->next);
  698.          }
  699.          goto recurse_entry;
  700.       }
  701.       else {
  702.          reply = local_reply;     /* Save this -- top level will need it. */
  703.          return(TRUE);
  704.       }
  705.    }
  706.    else if (local_reply == ui_concept_select) {
  707.       /* A concept is required.  Its index has been stored in uims_menu_index. */
  708.       deposit_concept(&concept_descriptor_table[uims_menu_index]);
  709.  
  710.       /* We ignore the value returned by deposit_concept.
  711.          If the user refused to enter a selector, no action is taken. */
  712.       goto recurse_entry;
  713.    }
  714.    else if (local_reply != ui_call_select) {
  715.       reply = local_reply;     /* Save this -- top level will need it. */
  716.       return(TRUE);
  717.    }
  718.  
  719.    /* We have a call.  Get the actual call and deposit it into the concept list. */
  720.  
  721.    if (not_interactive) {
  722.       if (initializing_database)
  723.          result = base_calls[1];     /* Get "nothing". */
  724.       else {
  725.          int i = generate_random_number(number_of_calls[parse_state.call_list_to_use]);
  726.          result = main_call_lists[parse_state.call_list_to_use][i];
  727.       }
  728.  
  729.       /* Why don't we just call the random number generator again if the call is inappropriate?
  730.          Wouldn't that be much faster?  There are two reasons:  First, we would need to take
  731.          special precautions against an infinite loop.  Second, and more importantly, if we
  732.          just called the random number generator again, it would screw up the hash numbers,
  733.          which would make the uniquefication fail, so we could see the same thing twice. */
  734.    
  735.       if (result->callflags & cflag__dont_use_in_resolve) fail("This shouldn't get printed.");
  736.    }
  737.    else {
  738.       result = main_call_lists[parse_state.call_list_to_use][uims_menu_index];
  739.    }
  740.  
  741.    if (deposit_call(result)) goto recurse_entry;
  742.  
  743.    /* Check our "stack" and see if we have recursive invocations to clean up. */
  744.  
  745.    if (parse_state.parse_stack_index != 0) {
  746.       /* Set stuff up for reading second call and its concepts. */
  747.    
  748.       /* Create a new parse block, point concept_write_ptr at its contents. */
  749.       /* Fill in the pointer to the second parse block. */
  750.    
  751.       parse_state.concept_write_ptr = parse_state.parse_stack[--parse_state.parse_stack_index].concept_write_save_ptr;
  752.  
  753.       (*parse_state.concept_write_ptr)->subsidiary_root = (parse_block *) 0;
  754.       parse_state.concept_write_base = &(*parse_state.concept_write_ptr)->subsidiary_root;
  755.       parse_state.concept_write_ptr = parse_state.concept_write_base;
  756.       *parse_state.concept_write_ptr = (parse_block *) 0;
  757.    
  758.       parse_state.call_list_to_use = call_list_any;
  759.    
  760.       switch (parse_state.parse_stack[parse_state.parse_stack_index].save_concept_kind) {
  761.          case concept_centers_and_ends:
  762.             parse_state.specialprompt = "ENTER CALL FOR ENDS --> ";
  763.             break;
  764.          case concept_on_your_own:
  765.             parse_state.specialprompt = "ENTER SECOND (CENTERS) CALL --> ";
  766.             break;
  767.          default:
  768.             parse_state.specialprompt = "ENTER SECOND CALL --> ";
  769.             break;
  770.       }
  771.    
  772.       parse_state.topcallflags = 0;          /* Erase anything we had -- it is meaningless now. */
  773.       goto recurse_entry;
  774.    }
  775.  
  776.    /* Advance the write pointer. */
  777.    parse_state.concept_write_ptr = &((*parse_state.concept_write_ptr)->next);
  778.  
  779.    return(FALSE);
  780. }
  781.  
  782.  
  783.  
  784. static int age_buckets[33];
  785.  
  786. static int mark_aged_calls(
  787.    int rr,
  788.    int dd,
  789.    int kk)
  790. {
  791.    int r, d, k;
  792.    int i, j, l, lomask, bits, lm, hibit, sum, remainder;
  793.  
  794.    r = rr;
  795.    d = dd;
  796.    k = kk;
  797.  
  798.    startover:
  799.  
  800.    lomask = (1<<k)-1;
  801.  
  802.    for (i=0; i<33; i++) age_buckets[i] = 0;
  803.  
  804.    for (i=0; i<number_of_calls[call_list_any]; i++) {
  805.       if ((main_call_lists[call_list_any][i]->age & (~lomask)) == r) {
  806.          bits = main_call_lists[call_list_any][i]->age & lomask;
  807.  
  808.          hibit = -1;
  809.          for (l=31, lm=0x80000000; l>=0; l--, lm>>=1) {
  810.             if (bits & lm) { hibit = l; break; }
  811.          }
  812.          age_buckets[hibit+1]++;
  813.       }
  814.    }
  815.  
  816.    /* The buckets are now filled. */
  817.  
  818.    sum = 0;
  819.    for (j=-1; j<32; j++) {
  820.       if ((sum + age_buckets[j+1]) > d) break;
  821.       sum += age_buckets[j+1];
  822.    }
  823.  
  824.    /* J tells how many buckets to mark, and sum tells how many items are in them. */
  825.  
  826.    remainder = d;
  827.  
  828.    if (j == -1) j = 0;
  829.  
  830.    for (i=0; i<number_of_calls[call_list_any]; i++) {
  831.       if ((main_call_lists[call_list_any][i]->age & (~lomask)) == r) {
  832.          bits = main_call_lists[call_list_any][i]->age & lomask;
  833.  
  834.          hibit = -1;
  835.          for (l=31, lm=0x80000000; l>=0; l--, lm>>=1) {
  836.             if (bits & lm) { hibit = l; break; }
  837.          }
  838.  
  839.          if (hibit < j) {
  840.             main_call_lists[call_list_any][i]->callflags |= 0x80000000;
  841.             remainder--;
  842.          }
  843.       }
  844.    }
  845.  
  846.    if (remainder > 0) {
  847.       if (j >= 0) {
  848.          r = r | (1<<j);
  849.          d = remainder;
  850.          k = j;
  851.          goto startover;
  852.       }
  853.    }
  854.  
  855.    return(remainder);
  856. }
  857.  
  858.  
  859. /* This is static to keep it from being lost by the longjmp. */
  860. static call_list_mode_t call_list_mode;
  861.  
  862. void main(int argc, char *argv[])
  863. {
  864.    int argno;
  865.  
  866.    /* This lets the X user interface intercept command line arguments that it is
  867.       interested in. */
  868.    uims_process_command_line(&argc, argv);
  869.  
  870.    /* Do general initializations, which currently consist only of
  871.       seeding the random number generator. */
  872.    general_initialize();
  873.  
  874.    calling_level = l_mainstream;    /* The default. */
  875.    call_list_mode = call_list_mode_none;
  876.  
  877.    for (argno=1; argno<argc; argno++) {
  878.       if (argv[argno][0] == '-') {
  879.          /* Special flag: must be one of
  880.             -write_list <filename>  -- write out the call list for the
  881.                   indicated level INSTEAD OF running the program
  882.             -write_full_list <filename>  -- write out the call list for the
  883.                   indicated level and all lower levels INSTEAD OF running the program
  884.             -abridge <filename>  -- read in the file, strike all the calls
  885.                   contained therein off the menus, and proceed. */
  886.  
  887.          if (strcmp(&argv[argno][1], "write_list") == 0)
  888.             call_list_mode = call_list_mode_writing;
  889.          else if (strcmp(&argv[argno][1], "write_full_list") == 0)
  890.             call_list_mode = call_list_mode_writing_full;
  891.          else if (strcmp(&argv[argno][1], "abridge") == 0)
  892.             call_list_mode = call_list_mode_abridging;
  893.          else
  894.             goto bad_flag;
  895.          argno++;
  896.          if (argno>=argc) goto bad_flag;
  897.          if (open_call_list_file(call_list_mode, argv[argno]))
  898.             exit_program(1);
  899.       }
  900.       else if (argv[argno][0] == 'm') calling_level = l_mainstream;
  901.       else if (argv[argno][0] == 'p') calling_level = l_plus;
  902.       else if (argv[argno][0] == 'a') {
  903.          if (argv[argno][1] == '1' && !argv[argno][2]) calling_level = l_a1;
  904.          else if (argv[argno][1] == '2' && !argv[argno][2]) calling_level = l_a2;
  905.          else if (argv[argno][1] == 'l' && argv[argno][2] == 'l' && !argv[argno][3]) calling_level = l_dontshow;
  906.          else
  907.             goto bad_level;
  908.       }
  909.       else if (argv[argno][0] == 'c' && argv[argno][1] == '3' && argv[argno][2] == 'a' && !argv[argno][3])
  910.          calling_level = l_c3a;
  911.       else if (argv[argno][0] == 'c' && argv[argno][1] == '3' && argv[argno][2] == 'x' && !argv[argno][3])
  912.          calling_level = l_c3x;
  913.       else if (argv[argno][0] == 'c' && argv[argno][1] == '4' && argv[argno][2] == 'a' && !argv[argno][3])
  914.          calling_level = l_c4a;
  915.       else if (argv[argno][0] == 'c' && !argv[argno][2]) {
  916.          if (argv[argno][1] == '1') calling_level = l_c1;
  917.          else if (argv[argno][1] == '2') calling_level = l_c2;
  918.          else if (argv[argno][1] == '3') calling_level = l_c3;
  919.          else if (argv[argno][1] == '4') calling_level = l_c4;
  920.          else
  921.             goto bad_level;
  922.       }
  923.       else
  924.          goto bad_level;
  925.    }
  926.  
  927.    /* initialize outfile_string to calling-level-specific default outfile */
  928.  
  929.    (void) strncpy(outfile_string, filename_strings[calling_level], MAX_FILENAME_LENGTH);
  930.  
  931.    initializing_database = TRUE;
  932.    testing_fidelity = FALSE;
  933.    not_interactive = TRUE;
  934.    parse_active_list = (parse_block *) 0;
  935.    parse_inactive_list = (parse_block *) 0;
  936.    
  937.    initialize_menus(call_list_mode);    /* This sets up max_base_calls. */
  938.  
  939.    /* If we wrote a call list file, that's all we do. */
  940.    if (call_list_mode == call_list_mode_writing || call_list_mode == call_list_mode_writing_full)
  941.       goto normal_exit;
  942.  
  943.    initialize_concept_sublists();
  944.  
  945.    uims_postinitialize();
  946.  
  947.    global_age = 1;
  948.  
  949.    /* Create the top level error handler. */
  950.  
  951.    longjmp_ptr = &longjmp_buffer;          /* point the global pointer at it. */
  952.    if (error_flag = setjmp(longjmp_buffer.the_buf)) {
  953.  
  954.       /* The call we were trying to do has failed.  Abort it and display the error message. */
  955.    
  956.       if (initializing_database) {
  957.          init_error(error_message1);
  958.          goto normal_exit;
  959.       }
  960.  
  961.       history[0] = history[history_ptr+1];     /* So failing call will get printed. */
  962.       history[0].warnings.bits[0] = 0;         /* But without any warnings we may have collected. */
  963.       history[0].warnings.bits[1] = 0;
  964.    
  965.       if (error_flag == 5) {
  966.          /* Special signal -- user clicked on special thing while trying to get subcall. */
  967.          if ((reply == ui_command_select) &&
  968.               ((uims_menu_index == command_quit) ||
  969.                (uims_menu_index == command_undo) ||
  970.                (uims_menu_index == command_abort)))
  971.             reply_pending = TRUE;
  972.             goto start_with_pending_reply;
  973.       }
  974.    
  975.       goto start_cycle;
  976.    }
  977.  
  978.    /* The tandem module wants to initialize some static tables. */
  979.  
  980.    initialize_tandem_tables();
  981.    
  982.    initializing_database = FALSE;
  983.    not_interactive = FALSE;
  984.  
  985.    /* HERE IS (APPROXIMATELY) WHERE THE PROGRAM STARTS. */
  986.    
  987.    clear_screen();
  988.  
  989.    writestuff("SD -- square dance caller's helper.");
  990.    newline();
  991.    writestuff("Copyright (c) 1991, 1992 William B. Ackerman and Stephen Gildea.");
  992.    newline();
  993.    newline();
  994.    writestuff("SD comes with ABSOLUTELY NO WARRANTY; for details see the license.");
  995.    newline();
  996.    writestuff("This is free software, and you are welcome to redistribute it ");
  997.    writestuff("under certain conditions; for details see the license.");
  998.    newline();
  999.    writestuff("You should have received a copy of the GNU General Public License ");
  1000.    writestuff("along with this program, in the file 'COPYING'; if not, write to ");
  1001.    writestuff("the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA ");
  1002.    writestuff("02139, USA.");
  1003.    newline();
  1004.    newline();
  1005.  
  1006.    show_banner:
  1007.  
  1008.    writestuff("Version ");
  1009.    writestuff(VERSION_STRING);
  1010.    writestuff(" : db");
  1011.    writestuff(major_database_version);
  1012.    writestuff(".");
  1013.    writestuff(minor_database_version);
  1014.    writestuff(" : ui");
  1015.    writestuff(uims_version_string());
  1016.    newline();
  1017.    writestuff("Output file is '");
  1018.    writestuff(outfile_string);
  1019.    writestuff("'");
  1020.    newline();
  1021.    
  1022.    new_sequence:
  1023.    
  1024.    /* Here to start a fresh sequence.  If first time, or if we got here by clicking on "abort",
  1025.       the screen has been cleared.  Otherwise, it shows the last sequence that we wrote. */
  1026.  
  1027.    /* Replace all the parse blocks left from the last sequence. */
  1028.    release_parse_blocks_to_mark((parse_block *) 0);
  1029.    
  1030.    /* Query for the starting setup. */
  1031.    
  1032.    reply = uims_get_command(mode_startup, call_list_any, FALSE);
  1033.    if (reply != ui_start_select) goto normal_exit;           /* Huh? */
  1034.    if (uims_menu_index == 0) goto normal_exit;
  1035.    
  1036.    history_ptr = 1;              /* Clear the position history. */
  1037.  
  1038.    whole_sequence_low_lim = 2;
  1039.    if (!startinfolist[uims_menu_index].into_the_middle) whole_sequence_low_lim = 1;
  1040.  
  1041.    history[1].warnings.bits[0] = 0;
  1042.    history[1].warnings.bits[1] = 0;
  1043.    history[1].draw_pic = FALSE;
  1044.    history[1].centersp = uims_menu_index;
  1045.    history[1].resolve_flag.kind = resolve_none;
  1046.    /* Put the people into their starting position. */
  1047.    history[1].state = startinfolist[uims_menu_index].the_setup;
  1048.    written_history_items = -1;
  1049.  
  1050.    error_flag = 0;
  1051.    
  1052.    /* Come here to read a bunch of concepts and a call and add an item to the history. */
  1053.    
  1054.    start_cycle:
  1055.    
  1056.    reply_pending = FALSE;
  1057.  
  1058.    start_with_pending_reply:
  1059.  
  1060.    allowing_modifications = 0;
  1061.    
  1062.    initialize_parse();
  1063.  
  1064.    /* Check for first call given to heads or sides only. */
  1065.    
  1066.    if ((history_ptr == 1) && startinfolist[history[1].centersp].into_the_middle)
  1067.       deposit_concept(¢ers_concept);
  1068.    
  1069.    /* Come here to get a concept or call or whatever from the user. */
  1070.    
  1071.    /* Display the menu and make a choice!!!! */
  1072.    
  1073.    restart_after_backup:
  1074.  
  1075.    /* Clear the warnings BEFORE we start depositing concepts.  The act of depositing
  1076.       concepts might log a warning. */
  1077.  
  1078.    history[history_ptr+1].warnings.bits[0] = 0;
  1079.    history[history_ptr+1].warnings.bits[1] = 0;
  1080.  
  1081.    if ((!reply_pending) && (!query_for_call())) {
  1082.       /* User specified a call (and perhaps concepts too). */
  1083.  
  1084.       /* The call to toplevelmove may make a call to "fail", which will get caught by the cleanup handler
  1085.          above, reset history_ptr, and go to start_cycle with the error message displayed. */
  1086.  
  1087.       toplevelmove();
  1088.       
  1089.       /* Call successfully completed and has been stored in history. */
  1090.       
  1091.       history_ptr++;
  1092.       goto start_cycle;
  1093.    }
  1094.    
  1095.    /* If get here, query_for_call exitted without completing its parse, because the operator
  1096.       selected something like "quit", "undo", or "resolve", or because we have such a command
  1097.       already pending. */
  1098.  
  1099.    if (reply == ui_command_select) {
  1100.       switch (uims_menu_index) {
  1101.          case command_quit:
  1102.             if (uims_do_abort_popup() != POPUP_ACCEPT) goto start_cycle;
  1103.             goto normal_exit;
  1104.          case command_abort:
  1105.             if (uims_do_abort_popup() != POPUP_ACCEPT) goto start_cycle;
  1106.             clear_screen();
  1107.             goto show_banner;
  1108.          case command_undo:
  1109.             {
  1110.             /* User wants to undo a call.  The concept parse list is not set up
  1111.                for easy backup, so we search forward from the beginning. */
  1112.  
  1113.                parse_block **this_ptr = parse_state.concept_write_base;
  1114.       
  1115.                if ((history_ptr == 1) && startinfolist[history[1].centersp].into_the_middle) this_ptr = &((*this_ptr)->next);
  1116.             
  1117.                for (;;) {
  1118.                   parse_block **last_ptr;
  1119.       
  1120.                   if (!*this_ptr) break;
  1121.             
  1122.                   last_ptr = this_ptr;
  1123.             
  1124.                   this_ptr = &((*this_ptr)->next);
  1125.       
  1126.                   if (this_ptr == parse_state.concept_write_ptr) {
  1127.                      parse_state.concept_write_ptr = last_ptr;
  1128.                      *last_ptr = (parse_block *) 0;
  1129.                      reply_pending = FALSE;
  1130.                      goto restart_after_backup;
  1131.                   }
  1132.       
  1133.                   if ((*last_ptr)->concept->kind <= marker_end_of_list) break;
  1134.                }
  1135.       
  1136.                /* We did not find our place, so we undo the whole line. */
  1137.                if (history_ptr > 1) history_ptr--;
  1138.                /* Going to start_cycle will make sure written_history_items does not exceed history_ptr. */
  1139.                goto start_cycle;
  1140.             }
  1141.          case command_save_pic:
  1142.             history[history_ptr].draw_pic = TRUE;
  1143.             /* We have to back up to BEFORE the item we just changed. */
  1144.             if (written_history_items > history_ptr-1)
  1145.                written_history_items = history_ptr-1;
  1146.             goto start_cycle;
  1147.          case command_change_outfile:
  1148.             {
  1149.                char newfile_string[MAX_FILENAME_LENGTH];
  1150.          
  1151.                if (uims_do_outfile_popup(newfile_string)) {
  1152.                   if ( (newfile_string[0] != '\0') && (strcmp(outfile_string, newfile_string) != 0) ) {
  1153.                      char confirm_message[MAX_FILENAME_LENGTH+25];
  1154.       
  1155.                      if (probe_file(newfile_string)) {
  1156.                         (void) strncpy(outfile_string, newfile_string, MAX_FILENAME_LENGTH);
  1157.                         (void) strncpy(confirm_message, "Output file changed to '", 25);
  1158.                         (void) strncat(confirm_message, outfile_string, MAX_FILENAME_LENGTH);
  1159.                         (void) strncat(confirm_message, "'", 2);
  1160.                         last_file_position = -1;
  1161.                         specialfail(confirm_message);
  1162.                      }
  1163.                      else {
  1164.                         specialfail("No write access to that file, no action taken.");
  1165.                      }
  1166.                   }
  1167.                }
  1168.                goto start_cycle;
  1169.             }
  1170.          case command_neglect:
  1171.             {
  1172.                char percentage_string[80];
  1173.                uims_reply local_reply;
  1174.                char title[80];
  1175.                int percentage, calls_to_mark, i, deficit, final_percent;
  1176.       
  1177.                if (uims_do_neglect_popup(percentage_string)) {
  1178.                   percentage = parse_number(percentage_string);
  1179.                   if ((percentage < 1) || (percentage > 99)) goto start_cycle;
  1180.                }
  1181.                else {
  1182.                   percentage = 25;
  1183.                }
  1184.       
  1185.                calls_to_mark = number_of_calls[call_list_any] * percentage / 100;
  1186.                if (calls_to_mark > number_of_calls[call_list_any])
  1187.                   calls_to_mark = number_of_calls[call_list_any];
  1188.       
  1189.                start_neglect:
  1190.       
  1191.                /* Clear all the marks. */
  1192.       
  1193.                for (i=0; i<number_of_calls[call_list_any]; i++) {
  1194.                   main_call_lists[call_list_any][i]->callflags &= ~0x80000000;
  1195.                }
  1196.       
  1197.                deficit = mark_aged_calls(0, calls_to_mark, 31);
  1198.       
  1199.                /* Determine percentage that were actually marked. */
  1200.       
  1201.                final_percent = ((calls_to_mark-deficit) * 100) / number_of_calls[call_list_any];
  1202.                if (final_percent > 100) final_percent = 100;
  1203.       
  1204.                fill_in_neglect_percentage(title, final_percent);
  1205.                clear_screen();
  1206.                writestuff(title);
  1207.                newline();
  1208.       
  1209.                /* Print the marked calls. */
  1210.             
  1211.                for (i=0; i<number_of_calls[call_list_any]; i++) {
  1212.                   if (main_call_lists[call_list_any][i]->callflags & 0x80000000) {
  1213.                      writestuff(main_call_lists[call_list_any][i]->name);
  1214.                      writestuff(", ");
  1215.                   }
  1216.                }
  1217.                newline();
  1218.       
  1219.                error_flag = 0;
  1220.                
  1221.                local_reply = uims_get_command(mode_normal, call_list_any, 0);
  1222.             
  1223.                if (local_reply == ui_call_select) {
  1224.                   /* Age this call. */
  1225.                   main_call_lists[call_list_any][uims_menu_index]->age = global_age;
  1226.                   goto start_neglect;
  1227.                }
  1228.                else
  1229.                   goto start_cycle;
  1230.             }
  1231.          case command_resolve: case command_reconcile: case command_anything: case command_nice_setup:
  1232.             {
  1233.                search_kind goal;
  1234.          
  1235.                switch (uims_menu_index) {
  1236.                   case command_resolve:
  1237.                      goal = search_resolve;
  1238.                      break;
  1239.                   case command_reconcile:
  1240.                      goal = search_reconcile;
  1241.                      break;
  1242.                   case command_anything:
  1243.                      goal = search_anything;
  1244.                      break;
  1245.                   case command_nice_setup:
  1246.                      goal = search_nice_setup;
  1247.                      break;
  1248.                }
  1249.             
  1250.                reply = full_resolve(goal);
  1251.          
  1252.                /* If full_resolve refused to operate (for example, we clicked on "reconcile"
  1253.                   when in an hourglass), it returned "ui_search_accept", which will cause
  1254.                   us simply to go to start_cycle. */
  1255.             
  1256.                /* If user clicked on something random, treat it as though he clicked on "accept"
  1257.                   followed by whatever it was. */
  1258.             
  1259.                if (reply == ui_command_select) {
  1260.                   switch (uims_menu_index) {
  1261.                      case command_quit: case command_abort: case command_getout:
  1262.                      case command_resolve: case command_reconcile: case command_anything: case command_nice_setup:
  1263.                         allowing_modifications = 0;
  1264.                         history[history_ptr+1].warnings.bits[0] = 0;
  1265.                         history[history_ptr+1].warnings.bits[1] = 0;
  1266.                         history[history_ptr+1].draw_pic = FALSE;
  1267.                         parse_state.concept_write_base = &history[history_ptr+1].command_root;
  1268.                         parse_state.concept_write_ptr = parse_state.concept_write_base;
  1269.                         *parse_state.concept_write_ptr = (parse_block *) 0;
  1270.                         reply_pending = TRUE;
  1271.                         /* Going to start_with_pending_reply will make sure written_history_items does not exceed history_ptr. */
  1272.                         goto start_with_pending_reply;
  1273.                   }
  1274.                }
  1275.       
  1276.                goto start_cycle;
  1277.             }
  1278.          case command_getout:
  1279.             {
  1280.                int getout_ind;
  1281.                char date[80];
  1282.                char header[80];
  1283.                int j;
  1284.             
  1285.                /* Check that it is really resolved. */
  1286.             
  1287.                if (history[history_ptr].resolve_flag.kind == resolve_none)
  1288.                   specialfail("This sequence is not resolved.");
  1289.             
  1290.                /* Put up the getout popup to see if the user wants to enter a header string. */
  1291.             
  1292.                getout_ind = uims_do_getout_popup(header);
  1293.          
  1294.                if (getout_ind == POPUP_DECLINE) goto start_cycle;    /* User didn't want to end this sequence after all. */
  1295.             
  1296.                /* User really wants this sequence.  Open the file and write it. */
  1297.             
  1298.                clear_screen();
  1299.                open_file();
  1300.                enable_file_writing = TRUE;
  1301.             
  1302.                doublespace_file();
  1303.  
  1304.                get_date(date);
  1305.                writestuff(date);
  1306.                writestuff("     ");
  1307.  
  1308.                /* log creation version info */
  1309.                writestuff("Sd");
  1310.                writestuff(VERSION_STRING);
  1311.                writestuff(":db");
  1312.                writestuff(major_database_version);
  1313.                writestuff(".");
  1314.                writestuff(minor_database_version);
  1315.                writestuff("     ");
  1316.  
  1317.                /* log level info */
  1318.                writestuff(getout_strings[calling_level]);
  1319.                if (call_list_mode == call_list_mode_abridging)
  1320.                    writestuff(" (abridged)");
  1321.  
  1322.                newline();
  1323.  
  1324.                if (getout_ind == POPUP_ACCEPT_WITH_STRING) {
  1325.                   writestuff("             ");
  1326.                   writestuff(header);
  1327.                   newline();
  1328.                }
  1329.          
  1330.                newline();
  1331.          
  1332.                for (j=whole_sequence_low_lim; j<=history_ptr; j++) write_history_line(j, (char *) 0, FALSE, file_write_double);
  1333.             
  1334.                doublespace_file();
  1335.                writestuff(resolve_names[history[history_ptr].resolve_flag.kind]);
  1336.                writestuff(resolve_distances[history[history_ptr].resolve_flag.distance & 7]);
  1337.          
  1338.                newline();
  1339.                enable_file_writing = FALSE;
  1340.                newline();
  1341.             
  1342.                close_file();     /* This will signal a "specialfail" if a file error occurs. */
  1343.       
  1344.                writestuff("Sequence written to '");
  1345.                writestuff(outfile_string);
  1346.                writestuff("'.");
  1347.                newline();
  1348.          
  1349.                global_age++;
  1350.       
  1351.                goto new_sequence;
  1352.             }
  1353.          default:
  1354.             goto normal_exit;
  1355.       }
  1356.    }
  1357.    else
  1358.       goto normal_exit;
  1359.    
  1360.    bad_level:
  1361.  
  1362.    print_line("Arg must be calling level: m, p, a1, a2, c1, c2, c3a, c3, c3x, c4a, or c4.");
  1363.    exit_program(1);
  1364.    
  1365.    bad_flag:
  1366.  
  1367.    print_line("Flag must be one of -write_list <filename>, -write_full_list <filename>, or -abridge <filename>.");
  1368.    exit_program(1);
  1369.  
  1370.    normal_exit:
  1371.    
  1372.    exit_program(0);
  1373. }
  1374.  
  1375.  
  1376.  
  1377.  
  1378. extern void get_real_subcall(
  1379.    parse_block *parseptr,
  1380.    by_def_item *item,
  1381.    final_set concin,
  1382.    parse_block **concptrout,
  1383.    callspec_block **callout,
  1384.    final_set *concout)
  1385.  
  1386. /* ****** needs to send out alternate_concept!!! */
  1387.  
  1388. {
  1389.    char tempstring_text[80];
  1390.    char *tempstringptr;
  1391.    parse_block *search;
  1392.    parse_block **newsearch;
  1393.    concept_descriptor *marker;
  1394.  
  1395.    /* Fill in defaults in case we choose not to get a replacement call. */
  1396.  
  1397.    *concptrout = parseptr;
  1398.    *callout = base_calls[item->call_id];
  1399.    *concout = concin;
  1400.    
  1401.    /* If this subcall invocation does not permit modification under any value of the
  1402.       "allowing_modifications" switch, we do nothing.  Just return the default.
  1403.       We do not search the list.  Hence, such subcalls are always protected
  1404.       from being substituted, and, if the same call appears multiple times
  1405.       in the derivation tree of a compound call, it will never be replaced.
  1406.       What do we mean by that?  Suppose we did a
  1407.          "[tally ho but [2/3 recycle]] the difference".
  1408.       After we create the table entry saying to change cast off 3/4 into 2/3
  1409.       recycle, we wouldn't want the cast off's in the difference getting
  1410.       modified.  Actually, that is not the real reason.  The casts are in
  1411.       different sublists.  The real reason is that the final part of mixed-up
  1412.       square thru is defined as 
  1413.          conc nullcall [mandatory_anycall] nullcall []
  1414.       and we don't want the unmodifiable nullcall that the ends are supposed to
  1415.       do getting modified, do we? */
  1416.  
  1417.    if (!(item->modifiers & (dfm_mandatory_anycall | dfm_or_anycall | dfm_allow_forced_mod)))
  1418.       return;
  1419.  
  1420.    /* See if we had something from before.  This avoids embarassment if a call is actually
  1421.       done multiple times.  We want to query the user just once and use that result everywhere.
  1422.       We accomplish this by keeping a subcall list showing what modifications the user
  1423.       supplied when we queried. */
  1424.  
  1425.    /* We ALWAYS search this list, if such exists.  Even if modifications are disabled.
  1426.       Why?  Because the reconciler re-executes calls after the point of insertion to test
  1427.       their fidelity with the new setup that results from the inserted calls.  If those
  1428.       calls have modified subcalls, we will find ourselves here, looking through the list.
  1429.       Modifications may have been enabled at the time the call was initially entered, but
  1430.       might not be now that we are being called from the reconciler. */
  1431.  
  1432.    if (parseptr->concept->kind == concept_another_call_next_mod) {
  1433.       newsearch = &parseptr->next;
  1434.  
  1435.       while (search = *newsearch) {
  1436.          if (base_calls[item->call_id] == search->call) {
  1437.             /* Found a reference to this call. */
  1438.             parse_block *subsidiary_ptr = search->subsidiary_root;
  1439.  
  1440.             /* If the pointer is nil, we already asked about this call,
  1441.                and the reply was no. */
  1442.             if (!subsidiary_ptr) return;
  1443.  
  1444.             *concptrout = subsidiary_ptr;
  1445.             *callout = NULLCALLSPEC;             /* ****** not right????. */
  1446.             *concout = 0;                        /* ****** not right????. */
  1447.             return;
  1448.          }
  1449.  
  1450.          newsearch = &search->next;
  1451.       }
  1452.    }
  1453.  
  1454.    /* Now we know that the list doesn't say anything about this call.  Perhaps we should
  1455.       query the user for a replacement and add something to the list.  First, decide whether
  1456.       we should consider doing so.  If we are initializing the
  1457.       database, the answer is always "no", even for calls that require a replacement call, such as
  1458.       "clover and anything".  This means that, for the purposes of database initialization,
  1459.       "clover and anything" is tested as "clover and nothing", since "nothing" is the subcall
  1460.       that appears in the database. */
  1461.       
  1462.    /* Of course, if we are testing the fidelity of later calls during a reconcile
  1463.       operation, we DO NOT EVER add any modifiers to the list, even if the user
  1464.       clicked on "allow modification" before clicking on "reconcile".  It is perfectly
  1465.       legal to click on "allow modification" before clicking on "reconcile".  It means
  1466.       we want modifications (chosen by random number generator, since we won't be
  1467.       interactive) for the calls that we randomly choose, but not for the later calls
  1468.       that we test for fidelity. */
  1469.  
  1470.    if (initializing_database | testing_fidelity) return;
  1471.  
  1472.    /* When we are searching for resolves and the like, the situation is different.  In this case,
  1473.       "initializing_database" is off but "not_interactive" is on.  We do perform mandatory
  1474.       modifications, so we will generate things like "clover and shakedown".  Of course, no
  1475.       querying actually takes place.  Instead, get_subcall just uses the random number generator.
  1476.       Therefore, whether resolving or in normal interactive mode, we are guided by the
  1477.       call modifier flags and the "allowing_modifications" global variable. */
  1478.  
  1479.    if (      (item->modifiers & dfm_mandatory_anycall) ||
  1480.              ((item->modifiers & dfm_or_anycall) && (allowing_modifications)) ||
  1481.              ((item->modifiers & dfm_allow_forced_mod) && (allowing_modifications > 1)))
  1482.       ;
  1483.    else
  1484.       return;     /* Do not query about this subcall.  Just return the default. */
  1485.  
  1486.    /* At this point, we know we should query the user about this call. */
  1487.       
  1488.    /* Set ourselves up for modification by making the null modification list
  1489.       if necessary.  ***** Someday this null list will always be present. */
  1490.  
  1491.    if (parseptr->concept->kind == marker_end_of_list) {
  1492.       parseptr->concept = &marker_concept_mod;
  1493.       newsearch = &parseptr->next;
  1494.    }
  1495.    else if (parseptr->concept->kind != concept_another_call_next_mod)
  1496.       fail("wrong marker in get_real_subcall???");
  1497.  
  1498.    /* Create a reference on the list.  "search" points to the null item at the end. */
  1499.  
  1500.    marker = &marker_concept_mod;
  1501.    if (dfm_allow_forced_mod & item->modifiers) marker = &marker_concept_force;
  1502.    else if (dfm_must_be_scoot_call & item->modifiers) marker = &marker_concept_modreact;
  1503.    else if (dfm_must_be_tag_call & item->modifiers) marker = &marker_concept_modtag;
  1504.  
  1505.    tempstringptr = tempstring_text;
  1506.    *tempstringptr = 0;           /* Null string, just to be safe. */
  1507.  
  1508.    /* If the replacement is mandatory, or we are not interactive,
  1509.       don't present the popup.  Just get the replacement call. */
  1510.  
  1511.    if (not_interactive)
  1512.       ;
  1513.    else if (dfm_mandatory_anycall & item->modifiers) {
  1514.       string_copy(&tempstringptr, "SUBSIDIARY CALL --> ");
  1515.    }
  1516.    else {
  1517.  
  1518.       /* Need to present the popup to the operator and find out whether modification
  1519.          is desired. */
  1520.  
  1521.       modify_popup_kind kind;
  1522.  
  1523.       if (item->modifiers & dfm_must_be_tag_call) kind = modify_popup_only_tag;
  1524.       else if (item->modifiers & dfm_must_be_scoot_call) kind = modify_popup_only_scoot;
  1525.       else kind = modify_popup_any;
  1526.  
  1527.       if (uims_do_modifier_popup(base_calls[item->call_id]->name, kind)) {
  1528.          /* User accepted the modification.
  1529.             Set up the prompt and get the concepts and call. */
  1530.       
  1531.          string_copy(&tempstringptr, "REPLACEMENT FOR THE ");
  1532.          string_copy(&tempstringptr, base_calls[item->call_id]->name);
  1533.          string_copy(&tempstringptr, " --> ");
  1534.       }
  1535.       else {
  1536.          /* User declined the modification.  Create a null entry so that we don't query again. */
  1537.          *newsearch = get_parse_block();
  1538.          (*newsearch)->concept = marker;
  1539.          (*newsearch)->call = base_calls[item->call_id];
  1540.          return;
  1541.       }
  1542.    }
  1543.  
  1544.    *newsearch = get_parse_block();
  1545.    (*newsearch)->concept = marker;
  1546.    (*newsearch)->call = base_calls[item->call_id];
  1547.  
  1548.    /* Set stuff up for reading subcall and its concepts. */
  1549.  
  1550.    /* Create a new parse block, point concept_write_ptr at its contents. */
  1551.    /* Create the new root at the start of the subsidiary list. */
  1552.  
  1553.    parse_state.concept_write_base = &(*newsearch)->subsidiary_root;
  1554.    parse_state.concept_write_ptr = parse_state.concept_write_base;
  1555.  
  1556.    parse_state.parse_stack_index = 0;
  1557.    parse_state.call_list_to_use = call_list_any;
  1558.    parse_state.specialprompt = tempstring_text;
  1559.  
  1560.    if (query_for_call())
  1561.       longjmp(longjmp_ptr->the_buf, 5);     /* User clicked on something unusual like "exit" or "undo". */
  1562.  
  1563.    *concptrout = (*newsearch)->subsidiary_root;
  1564.    *callout = NULLCALLSPEC;              /* We THROW AWAY the alternate call, because we want our user to get it from the concept list. */
  1565.    *concout = 0;
  1566. }
  1567.